#include "RTSCamera.h"

#include <iostream>

#include "glm\\gtc\\constants.hpp"
#include "glm\\gtx\\rotate_vector.hpp"

RTSCamera::RTSCamera(bool input)
{
	minXRot = 0.0f;
	maxXRot = 90.0f;
	maxYRot = 360.0f;

	distanceToFocus = 5.0f;
	zoomModifier = 0.2f;

	xRot = 0.0f;
	yRot = 0.0f;

	up = glm::vec3(0.0f, 1.0f, 0.0f);
	lookAt = glm::vec3(0.0f, 0.0f, 0.0f);
	position = glm::vec3(0.0f, 0.0f, distanceToFocus);
	forward = glm::normalize(lookAt - position);

	UpdateCameraMatrix();
	mouseMotionStarted = false;

	if(input)
	{
		InputListener::inputStack.push(std::shared_ptr<InputListener>(this));
	}
}

RTSCamera::~RTSCamera()
{

}

void RTSCamera::Update()
{

}

void RTSCamera::ProcessInputQueue()
{
	int inputCount = InputQueueSize();
	if(inputCount > 0)
	{
		std::shared_ptr<Input> input = Peek();
		if(input->type == MousePress)
		{
			mouseMotionStarted = true;
			inputBuffer.push_back(std::dynamic_pointer_cast<MouseInput>(input));
		}
		else if(input->type == MouseRelease)
		{
			mouseMotionStarted = false;
			inputBuffer.push_back(std::dynamic_pointer_cast<MouseInput>(input));
		}
		else if(mouseMotionStarted)
		{
			inputBuffer.push_back(std::dynamic_pointer_cast<MouseInput>(input));
			UpdateCamera();
		}
		else if(input->type == MouseWheel)
		{
			std::shared_ptr<MouseInput> mouseInput = std::dynamic_pointer_cast<MouseInput>(input);
			distanceToFocus = glm::max(1.0f, distanceToFocus - mouseInput->delta);
		}

		DequeueInput();
	}
}

void RTSCamera::UpdateCamera()
{
	std::shared_ptr<MouseInput> currentInput = inputBuffer.at(inputBuffer.size() - 1);
	std::shared_ptr<MouseInput> previousInput = inputBuffer.at(inputBuffer.size() - 2);

	glm::vec2 currentMouse = glm::vec2(currentInput->x, currentInput->y);
	glm::vec2 previousMouse = glm::vec2(previousInput->x, previousInput->y);
	
	float magnitude = (currentMouse - previousMouse).length() / glm::pi<float>() * 2;
	
	glm::vec2 mouseDelta = currentMouse - previousMouse;

	xRot += mouseDelta.y * 0.1f;
	yRot += mouseDelta.x * 0.1f;

	xRot = std::max(std::min(xRot, maxXRot), minXRot);
	yRot = std::fmod(yRot, maxYRot);
}

void RTSCamera::UpdateCameraMatrix()
{
	float xRotRad = xRot / 180.0f * glm::pi<float>();
	float yRotRad = -yRot / 180.0f * glm::pi<float>();

	//spherical coordinates to calculate the forward vector
	position.x = cos(xRotRad) * sin(yRotRad);
	position.y = sin(xRotRad);
	position.z = cos(xRotRad) * cos(yRotRad);
	position = position * distanceToFocus;

	forward = lookAt - position;
	forward = glm::normalize(forward);

	if((xRot > 89.99f && xRot < 269.99f )|| (xRot < -89.99f && xRot > -269.99f))
	{
		up = glm::vec3(0, -1, 0);
	}
	else
	{
		up = glm::vec3(0, 1, 0);
	}


	glm::vec3 right = glm::normalize(glm::cross(forward, up));
	up = glm::normalize(glm::cross(right, forward));

	glm::mat4 cameraMatrix = glm::mat4(1.0f);
	cameraMatrix[0][0] = right.x;
	cameraMatrix[0][1] = right.y;
	cameraMatrix[0][2] = right.z;
	
	cameraMatrix[1][0] = up.x;
	cameraMatrix[1][1] = up.y;
	cameraMatrix[1][2] = up.z;
	
	//camera coordinate is left handed, opposite to the rest, so negative for forward
	cameraMatrix[2][0] = -forward.x;
	cameraMatrix[2][1] = -forward.y;
	cameraMatrix[2][2] = -forward.z;

	cameraMatrix[3][0] = position.x;
	cameraMatrix[3][1] = position.y;
	cameraMatrix[3][2] = position.z;
	cameraMatrix[3][3] = 1.0f;


	transform->SetModelMatrix(cameraMatrix);

	viewMatrix = glm::inverse(transform->GetModelMatrix());
}